#include "stdafx.h"
#include <LitStream.h>

namespace Lit
{
	class LIT_API MemoryStreamPrivates
	{
		friend class LIT_API MemoryStream;
	public:
			
		MemoryStreamPrivates(){}
		MemoryStreamPrivates(const String &str) : stream(str.data()){}
		~MemoryStreamPrivates(){}

		MemoryStreamPrivates& operator=( const String& str ){
			stream.str( str.data() );
			return *this;
		}

	protected:
#ifdef LIT_UNICODE
		std::wstringstream stream;
#else
		std::stringstream stream;
#endif
	};
		
	void MemoryStream::_alloc(){ _dealloc(); mPrivates = new MemoryStreamPrivates; }
	void MemoryStream::_alloc( const String & content ){ _dealloc(); mPrivates = new MemoryStreamPrivates( content ); }

	void MemoryStream::_dealloc(){
		if( mPrivates != nullptr )
			delete mPrivates, mPrivates = nullptr;
	}
	Stream::Stream( Access access ): mAccess( access ), Named("unknown"){}
	Stream::Stream( const String &name, Access access ) : mAccess(access), Named(name){}
	Stream::Stream( const Stream &rk ) : mAccess(rk.mAccess), Named(rk.mName){}
	Stream::~Stream(){}

	Stream::Access Stream::getAccess() const{ return mAccess; }
	std::ios::open_mode Stream::getStdAccess() const{ 
		std::ios_base::open_mode ret;
		if( mAccess & WriteAccess ) ret = std::ios::out;
		else ret = std::ios::in;
			
		if( mAccess & BinaryAccess ) ret |= std::ios::binary;
			
		return ret;
	}
	String Stream::readLine()
	{
		String l;
		readUntil('\n', l);
		return l;
	}
	String Stream::readAll()
	{ 
		String ret( size() + 1 );
		read( size(), ret );
		return ret;
	}

	MemoryStream::MemoryStream( Access access ):Stream( access ){}
	MemoryStream::MemoryStream( const String &content, Access access  ) : Stream("_unknown_", access){ _alloc( content ); }
	MemoryStream::MemoryStream( const String &name, const String &content, Access access  ) : Stream(name, access){ _alloc(content); }
	MemoryStream::MemoryStream( const MemoryStream &rk ) : Stream(rk){ _alloc( rk.mPrivates->stream.str().c_str() ); }
	MemoryStream::~MemoryStream(){ _dealloc(); }

	size_t MemoryStream::size() const{ return mPrivates->stream.str().size(); }
	void MemoryStream::close(){}
	bool MemoryStream::open( const String &name, Access access )
	{
		mName = name;
		mAccess = access;
		return true;
	}
	MemoryStream& MemoryStream::read( size_t num_chars, String & out ){ 
		out.resize( num_chars+1 );
		mPrivates->stream.read( &out.at(0), num_chars );
		out[num_chars] = '\0';
		return *this;
	}
	MemoryStream& MemoryStream::write( const String& str ){
		mPrivates->stream.write( &str.at(0), str.size() );
		return *this;
	}
	MemoryStream& MemoryStream::readUntil( char c, String & out ){
		char cur = 0;
		size_t i=0;
		while(  !eof() && (cur = mPrivates->stream.get()) != c )
		{
			out+=cur;
			i++;
		}
		if( !out.empty() ) out[i==0?0:(i-1)] = '\0';
		return *this;
	}
	bool MemoryStream::eof() const{ return mPrivates->stream.eof(); }
	bool MemoryStream::valid() const { return mPrivates->stream.good(); }

	FileStream::FileStream( ):Stream( ){}
	FileStream::FileStream( const String &name, Access access ) : Stream(name, access){ open( name, access ); }
	FileStream::FileStream( const FileStream &rk ) : Stream(rk){}
	FileStream::~FileStream(){}
		
	std::fstream& FileStream::handle(){ return mStream; }
	const std::fstream& FileStream::handle() const{ return mStream; }

	size_t FileStream::size() const{ return mSize; }
	void FileStream::close(){ mStream.close(); }
	bool FileStream::open( const String &name, Access access )
	{
		if( !name.empty() )
		{
			mName = name;
			mAccess = access;
				
			mStream.open( name.data(), getStdAccess() );
			if( !mStream.is_open() ) return false;
			if( access & WriteAccess )
			{
				mStream.seekp( 0, std::ios::end );
				mSize = mStream.tellp();
				mStream.seekp( 0, std::ios::beg );
			}
			else
			{
				mStream.seekg( 0, std::ios::end );
				mSize = mStream.tellg();
				mStream.seekg( 0, std::ios::beg );
			}
			return true;
		}
		return false;
	}
	FileStream& FileStream::read( size_t num_chars, String & out ){ 
		out.resize( num_chars+1 );
		mStream.read( &out.at(0), num_chars );
		out[num_chars] = '\0';
		return *this;
	}
	FileStream& FileStream::write( const String& str ){
		mStream.write( &str.at(0), str.size() );
		return *this;
	}
	FileStream& FileStream::readUntil( char c, String & out ){
		char cur = 0;
		size_t i=0;
		while(  !eof() && (cur = mStream.get()) != c )
		{
			out+=cur;
			i++;
		}
		if( !out.empty() ) out[i==0?0:(i-1)] = '\0';
		return *this;
	}
	bool FileStream::eof() const{ return mStream.eof(); }
	bool FileStream::valid() const { return mStream.good(); }
};